﻿#if UNITY_2018_4_OR_NEWER
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
using System.Collections.Generic;
using System;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.AddressableAssets.ResourceLocators;
using System.IO;
using System.Threading;

public class AddressablesUtil
{
    public System.Action<UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle<UnityEngine.GameObject>> mGameObjCall;
    static Dictionary<string, bool> mLocalDic = new Dictionary<string, bool>();
    static Dictionary<string, bool> mLocalCurBundDic = new Dictionary<string, bool>();
    static string persistentDataPath;

    static Thread clearThread;

    public static void InitLocalOldDic()
    {

    }

    public static void InitLocalDic()
    {
        IEnumerable<IResourceLocator> locators0 = Addressables.ResourceLocators;

        mLocalCurBundDic.Clear();
        //暴力遍历所有的key
        foreach (var locator in locators0)
        {
            if(null != locator)
            {
                foreach (var key in locator.Keys)
                {
                    if (key == null)
                        continue;
                    bool tmpB;
                    string uKey = key as string;
                    if (string.IsNullOrEmpty(uKey))
                        continue;

                    bool startClinentRes = uKey.StartsWith("ResLod/") || uKey.StartsWith("ClientRes/") || uKey.StartsWith("Assets/");
                    if (startClinentRes && !mLocalDic.TryGetValue(uKey, out tmpB))
                    {
                        mLocalDic.Add(uKey, true);
                    }

                    bool isBundle = uKey.EndsWith(".bundle");

                    if (isBundle)
                    {
                        int sIndex = uKey.LastIndexOf("_");
                        string hash = uKey.Substring(sIndex + 1).Replace(".bundle","");

                        if(!mLocalCurBundDic.TryGetValue(hash, out tmpB))   mLocalCurBundDic.Add(hash, true);
                    }
                }
            }
        }


        clearThread = new Thread(new ThreadStart(ClearCacheAddress));
        clearThread.IsBackground = true;
        clearThread.Start();

        persistentDataPath = Application.persistentDataPath+"/Cache";
    }

    public static void ClearCacheAddress()
    {
        if (mLocalCurBundDic == null)
            return;

        if (!Directory.Exists(persistentDataPath))
            return;

        bool tmpB;
        string[] files = Directory.GetDirectories(persistentDataPath,"*.*");

        int len = files.Length;
        for(int i =0;i < len;i++)
        {
            string file = files[i];

            string[] itmFiles = Directory.GetDirectories(file, "*.*");
            int fnt = itmFiles.Length;
            int count = 0;
            for (int f = 0; f < fnt; f++)
            {
                string itemFile = itmFiles[f];
                string dirName = Path.GetFileName(itemFile);
                if (!mLocalCurBundDic.TryGetValue(dirName, out tmpB))
                {
                    count++;
                    Directory.Delete(itemFile, true);

                    //string[] cFiles = Directory.GetFiles(itemFile, "*.*");
                    //int cnt = cFiles.Length;
                    //for (int c = 0; c < cnt; c++)
                    //{
                    //    FileInfo fno = new FileInfo(cFiles[c]);
                    //    if(fno.Length > 0)
                    //        File.Delete(cFiles[c]);
                    //    else
                    //    {
                    //        count = -1;
                    //        break;
                    //    }
                    //}
                }
            }

            //if (fnt == 0 || count == fnt)
            //{
            //    Directory.Delete(file, true);
            //}
        }

        clearThread.Abort();

        clearThread = null;
        mLocalCurBundDic = null;
    }

    public static bool HaveAddress(string key)
    {
        if (mLocalDic == null || key == null)
            return true;

        bool hb;
        return mLocalDic.TryGetValue(key,out hb);
    }

    /// <summary>
    /// 此方法xlua没有生成
    /// </summary>
    /// <param name="path"></param>
    /// <returns></returns>
    public static System.Object LoadAssetAsync(string path,System.Action call = null)
    {
        AsyncOperationHandle<System.Object> handle = Addressables.LoadAssetAsync<System.Object>(path);
        handle.Completed += (op) => {

            if(null != call)
                call();
        };
        return handle; 
    }

    public static AsyncOperationHandle<Texture2D> LoadTexture2DAsync(string path, System.Action call = null)
    {
        AsyncOperationHandle<Texture2D> handle = Addressables.LoadAssetAsync<Texture2D>(path);
        handle.Completed += (op) => {

            if (null != call)
                call();
        };
        return handle; 
    }

    public static AsyncOperationHandle<Texture2D> LoadTexture2DAsyncCallHandle(string path, System.Action<AsyncOperationHandle<Texture2D>> call)
    {
        AsyncOperationHandle<Texture2D> handle = Addressables.LoadAssetAsync<Texture2D>(path);
        handle.Completed += (op) => {

            if (null != call)
                call(op);
        };
        return handle;
    }

    public static AsyncOperationHandle<TextAsset> LoadTextAssetAsync(string path, System.Action call = null)
    {
        AsyncOperationHandle<TextAsset> handle = Addressables.LoadAssetAsync<TextAsset>(path);
        handle.Completed += (op) => {

            if (null != call)
                call();
        };
        return handle;
    }

    public static AsyncOperationHandle<UnityEngine.Font> LoadFontAssetAsync(string path, System.Action call = null)
    {
        AsyncOperationHandle<UnityEngine.Font> handle = Addressables.LoadAssetAsync<UnityEngine.Font>(path);
        handle.Completed += (op) => {

            if (null != call)
                call();
        };
        return handle;
    }

    public static void LoadShaderVariantsAsync(string path, System.Action<ShaderVariantCollection> call = null)
    {
        AsyncOperationHandle<UnityEngine.ShaderVariantCollection> handle = Addressables.LoadAssetAsync<UnityEngine.ShaderVariantCollection>(path);
        handle.Completed += (op) => {
            if (null != call)
                call(op.Result);
        }; 
    }

    public static AsyncOperationHandle<SceneInstance> LoadSceneAsync(string path,System.Action<AsyncOperationHandle<SceneInstance>> completed)
    {
        var handle = Addressables.LoadSceneAsync(path);

        handle.Completed += completed;
        return handle;
    }

    public static void ReleaseTexture2D(AsyncOperationHandle<Texture2D> handle)
    {
        if (!handle.IsValid())
        {
            return;
        }

        if (!handle.IsDone)
            return;

        Addressables.ReleaseInstance(handle);
    }

    public static void ReleaseInstance(AsyncOperationHandle handle)
    {
        if(!handle.IsValid())
        {
            return;
        }

        if (!handle.IsDone)
        {
            Addressables.Release(handle);
            return;
        }

        Addressables.ReleaseInstance(handle);
    }

    public static AsyncOperationHandle<GameObject> InstAsync(string path, System.Action<GameObject> call)
    {
       
        AsyncOperationHandle<GameObject> handle = Addressables.InstantiateAsync(path);
        handle.Completed += (op) => {

            call(handle.Result);
    
        };

        return handle;
    }

    public static AsyncOperationHandle<GameObject> InstAsync(string path, Vector3 position, Quaternion rotation, Transform parent = null, System.Action<GameObject> call = null)
    {


        if (!HaveAddress(path))
        {
            call(null);

            return default(AsyncOperationHandle<GameObject>);
        }
           

        AsyncOperationHandle<GameObject> handle = Addressables.InstantiateAsync(path,position,rotation,parent);
        handle.Completed += (op) => {

            call(handle.Result);
          
        };

        return handle;
    }

    public static void DownloadDependenciesAsync(string key,System.Action  call = null)
    {
        AsyncOperationHandle handle = Addressables.DownloadDependenciesAsync(key);

        handle.Completed += (op) => {

            call();
        };
    }

    //==========================================================================
    struct S_AddressLoadItem
    {
        public string Path;
        public Vector3 OrgPos;
        public Quaternion OrgQua;
        public bool OrgVisible;
        public GameObject Object;
        public AsyncOperationHandle<GameObject> Handle;
        public List<System.Action<GameObject>> CallList;
    }

    static Dictionary<string, S_AddressLoadItem> InstLoadingDic = new Dictionary<string, S_AddressLoadItem>();

    public static GameObject CacheObject(string path)
    {
        S_AddressLoadItem itm;
        if (InstLoadingDic.TryGetValue(path, out itm) && itm.Handle.IsValid())
        {
            return itm.Handle.Result;
        }

        return null;
    }

    //=======================================================================================
    static object EvaluateKey(object obj)
    {
        if (obj is IKeyEvaluator)
            return (obj as IKeyEvaluator).RuntimeKey;
        return obj;
    }

    /// <summary>获取总Size</summary>
    /// <param name="keys"></param>
    /// <returns></returns>
    public static AsyncOperationHandle<long> GetTotalSizeAsync(IList<object> keys)
    {
        List<IResourceLocation> allLocations = new List<IResourceLocation>();
        foreach (object key in keys)
        {
            IList<IResourceLocation> locations;
            if (key is IList<IResourceLocation>)
                locations = key as IList<IResourceLocation>;
            else if (key is IResourceLocation)
            {
                locations = new List<IResourceLocation>(1)
                    {
                        key as IResourceLocation
                    };
            }
            else if (!GetResourceLocations(key, typeof(object), out locations))
                return Addressables.ResourceManager.CreateCompletedOperation<long>(0, new InvalidKeyException(key, typeof(object)).Message);

            foreach (var loc in locations)
            {
                if (loc.HasDependencies)
                    allLocations.AddRange(loc.Dependencies);
            }
        }

        long size = 0;
        foreach (IResourceLocation location in allLocations)
        {
            var sizeData = location.Data as AssetBundleRequestOptions;
            if (sizeData != null)
                size += sizeData.BundleSize;
        }

        return Addressables.ResourceManager.CreateCompletedOperation<long>(size, string.Empty);
    }

    static bool GetResourceLocations(object key, Type type, out IList<IResourceLocation> locations)
    {
        key =  EvaluateKey(key);

        locations = null;
        HashSet<IResourceLocation> current = null;
        foreach (var locatorInfo in Addressables.ResourceLocators)
        {
            var locator = locatorInfo;
            IList<IResourceLocation> locs;
            if (locator.Locate(key, type, out locs))
            {
                if (locations == null)
                {
                    //simple, common case, no allocations
                    locations = locs;
                }
                else
                {
                    //less common, need to merge...
                    if (current == null)
                    {
                        current = new HashSet<IResourceLocation>();
                        foreach (var loc in locations)
                            current.Add(loc);
                    }

                    current.UnionWith(locs);
                }
            }
        }

        if (current == null)
            return locations != null;

        locations = new List<IResourceLocation>(current);
        return true;
    }

    static bool GetResourceLocations(IEnumerable<object> keys, Type type, Addressables.MergeMode merge, out IList<IResourceLocation> locations)
    {
        locations = null;
        HashSet<IResourceLocation> current = null;
        foreach (var key in keys)
        {
            IList<IResourceLocation> locs;
            if (GetResourceLocations(key, type, out locs))
            {
                if (locations == null)
                {
                    locations = locs;
                    if (merge == Addressables.MergeMode.UseFirst)
                        return true;
                }
                else
                {
                    if (current == null)
                    {
                        current = new HashSet<IResourceLocation>(locations);
                    }

                    if (merge == Addressables.MergeMode.Intersection)
                        current.IntersectWith(locs);
                    else if (merge == Addressables.MergeMode.Union)
                        current.UnionWith(locs);
                }
            }
            else
            {
                //if entries for a key are not found, the intersection is empty
                if (merge == Addressables.MergeMode.Intersection)
                {
                    locations = null;
                    return false;
                }
            }
        }

        if (current == null)
            return locations != null;
        if (current.Count == 0)
        {
            locations = null;
            return false;
        }
        locations = new List<IResourceLocation>(current);
        return true;
    }

    public static List<IResourceLocation> GetDepyes(IList<object> keys)
    {
        List<IResourceLocation> allLocations = new List<IResourceLocation>();
        foreach (object key in keys)
        {
            IList<IResourceLocation> locations;
            if (key is IList<IResourceLocation>)
                locations = key as IList<IResourceLocation>;
            else if (key is IResourceLocation)
            {
                locations = new List<IResourceLocation>(1)
                    {
                        key as IResourceLocation
                    };
            }
            else if (!GetResourceLocations(key, typeof(object), out locations))
            {

            }

            foreach (var loc in locations)
            {
                if (loc.HasDependencies)
                    allLocations.AddRange(loc.Dependencies);
            }
        }

        return allLocations;
    }

#if UNITY_EDITOR
    /// <summary>编辑器用</summary>
    /// <param name="catalogPath">目录路径</param>
    /// <param name="key">address key</param>
    /// <returns></returns>
    public static List<IResourceLocation> GetDependenciesByCatalog(string catalogPath,string key)
    {
        string textAsset = File.ReadAllText(catalogPath);
        ContentCatalogData data = JsonUtility.FromJson<ContentCatalogData>(textAsset);
        ResourceLocationMap map = data.CreateLocator();
        Type type = typeof(object);

        IList<IResourceLocation> locs;
        if (map.Locate(key, type, out locs))
        {

        }

        List<IResourceLocation> allLocations = new List<IResourceLocation>();
        foreach (var loc in locs)
        {
            if (loc.HasDependencies)
                allLocations.AddRange(loc.Dependencies);
        }

        return allLocations;
    }
#endif
}
#endif
